/*   Copyright © 2009 Mark Gil Torres
 *
 *   This file is part of mtoolkit.
 *
 *   mtoolkit is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   any later version.
 *
 *   mtoolkit is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with mtoolkit.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef RATIONAL_H
#define RATIONAL_H

#include "integer.h"
#include <string>

using namespace std;

namespace mt {

  class Rational {
    public:
      Rational(void);
      Rational(Rational const& inValue);
      Rational(int const& inValue);
      Rational(string const& inValue);
      Rational(int const& inValue1, int const& inValue2);
      Rational(string const& inValue1, string const& inValue2);
      Rational(Integer const& inValue1, Integer const& inValue2);

      Rational& operator=(Rational const& inValue);

      Integer const getNumerator();
      Integer const getDenominator();

      void setNumerator(int const& inValue);
      void setNumerator(string const& inValue);
      void setNumerator(Integer const& inValue);
      void setDenominator(int const& inValue);
      void setDenominator(string const& inValue);
      void setDenominator(Integer const& inValue);

      friend Rational const operator+(Rational const& inValue1, int const& inValue2);
      friend Rational const operator-(Rational const& inValue1, int const& inValue2);
      friend Rational const operator*(Rational const& inValue1, int const& inValue2);
      friend Rational const operator/(Rational const& inValue1, int const& inValue2);
      friend Rational const operator+(int const& inValue1, Rational const& inValue2);
      friend Rational const operator-(int const& inValue1, Rational const& inValue2);
      friend Rational const operator*(int const& inValue1, Rational const& inValue2);
      friend Rational const operator/(int const& inValue1, Rational const& inValue2);

      friend Rational const operator+(Rational const& inValue1, Rational const& inValue2);
      friend Rational const operator-(Rational const& inValue1, Rational const& inValue2);
      friend Rational const operator*(Rational const& inValue1, Rational const& inValue2);
      friend Rational const operator/(Rational const& inValue1, Rational const& inValue2);

      friend bool operator<(Rational const& inValue1, int const& inValue2);
      friend bool operator<=(Rational const& inValue1, int const& inValue2);
      friend bool operator>(Rational const& inValue1, int const& inValue2);
      friend bool operator>=(Rational const& inValue1, int const& inValue2);
      friend bool operator==(Rational const& inValue1, int const& inValue2);
      friend bool operator<(int const& inValue1, Rational const& inValue2);
      friend bool operator<=(int const& inValue1, Rational const& inValue2);
      friend bool operator>(int const& inValue1, Rational const& inValue2);
      friend bool operator>=(int const& inValue1, Rational const& inValue2);
      friend bool operator==(int const& inValue1, Rational const& inValue2);
      friend bool operator<(Rational const& inValue1, Integer const& inValue2);
      friend bool operator<=(Rational const& inValue1, Integer const& inValue2);
      friend bool operator>(Rational const& inValue1, Integer const& inValue2);
      friend bool operator>=(Rational const& inValue1, Integer const& inValue2);
      friend bool operator==(Rational const& inValue1, Integer const& inValue2);
      friend bool operator<(Integer const& inValue1, Rational const& inValue2);
      friend bool operator<=(Integer const& inValue1, Rational const& inValue2);
      friend bool operator>(Integer const& inValue1, Rational const& inValue2);
      friend bool operator>=(Integer const& inValue1, Rational const& inValue2);
      friend bool operator==(Integer const& inValue1, Rational const& inValue2);
      friend bool operator<(Rational const& inValue1, Rational const& inValue2);
      friend bool operator<=(Rational const& inValue1, Rational const& inValue2);
      friend bool operator>(Rational const& inValue1, Rational const& inValue2);
      friend bool operator>=(Rational const& inValue1, Rational const& inValue2);
      friend bool operator==(Rational const& inValue1, Rational const& inValue2);

      friend Rational convertQFromZ(Integer const& inValue);
    private:
      void setLowestTerm();
      void checkDenominator(); //if dValue is negative, get abs(dValue) and set nValue = -1*nValue;
      Integer nValue;
      Integer dValue;
  };

  Rational::Rational(void) {
    nValue=0;
    dValue=1;
  }

  Rational::Rational(Rational const& inValue) : nValue(inValue.nValue), dValue(inValue.dValue) {}

  Rational::Rational(int const& inValue) {
    nValue = inValue;
    dValue = 1;
  }

  Rational::Rational(string const& inValue) {
    nValue = inValue;
    dValue = 1;
  }

  Rational::Rational(int const& inValue1, int const& inValue2) {
    nValue = inValue1;
    dValue = inValue2;
    checkDenominator();
    setLowestTerm();
  }

  Rational::Rational(string const& inValue1, string const& inValue2) {
    nValue = inValue1;
    dValue = inValue2;
    checkDenominator();
    setLowestTerm();
  }

  Rational::Rational(Integer const& inValue1, Integer const& inValue2) {
    nValue = inValue1;
    dValue = inValue2;
    checkDenominator();
    setLowestTerm();
  }

  Rational& Rational::operator=(Rational const& inValue) {
    nValue = inValue.nValue;
    dValue = inValue.dValue;
    return *this;
  }

  Integer const Rational::getNumerator() {
    return nValue;
  }

  Integer const Rational::getDenominator() {
    return dValue;
  }

  void Rational::setNumerator(int const& inValue) {
    nValue = inValue;
    this->setLowestTerm();
  }

  void Rational::setNumerator(string const& inValue) {
    nValue = inValue;
    this->setLowestTerm();
  }

  void Rational::setNumerator(Integer const& inValue) {
    nValue = inValue;
    this->setLowestTerm();
  }

  void Rational::setDenominator(int const& inValue) {
    dValue = inValue;
    this->setLowestTerm();
  }

  void Rational::setDenominator(string const& inValue) {
    dValue = inValue;
    this->setLowestTerm();
  }

  void Rational::setDenominator(Integer const& inValue) {
    dValue = inValue;
    this->setLowestTerm();
  }

//arithmetic operators

  Rational const operator+(Rational const& inValue1, int const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue + (inValue2 * inValue1.dValue);
    temp.dValue = inValue1.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator-(Rational const& inValue1, int const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue - (inValue2 * inValue1.dValue);
    temp.dValue = inValue1.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator*(Rational const& inValue1, int const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue * inValue2;
    temp.dValue = inValue1.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator/(Rational const& inValue1, int const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue;
    temp.dValue = inValue1.dValue * inValue2;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator+(int const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = (inValue1 * inValue2.dValue) + inValue2.nValue;
    temp.dValue = inValue2.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator-(int const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = (inValue1 * inValue2.dValue) - inValue2.nValue;
    temp.dValue = inValue2.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator*(int const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = inValue1 * inValue2.nValue;
    temp.dValue = inValue2.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator/(int const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = inValue1 * inValue2.dValue;
    temp.dValue = inValue2.nValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator+(Rational const& inValue1, Rational const& inValue2) {
    Rational temp;
    Integer tempLCM;
    tempLCM = lcm(inValue1.dValue,inValue2.dValue);
    temp.nValue = ((tempLCM / inValue1.dValue) * inValue1.nValue) + ((tempLCM / inValue2.dValue) * inValue2.nValue);
    temp.dValue = tempLCM;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator-(Rational const& inValue1, Rational const& inValue2) {
    Rational temp;
    Integer tempLCM;
    tempLCM = lcm(inValue1.dValue,inValue2.dValue);
    temp.nValue = ((tempLCM / inValue1.dValue) * inValue1.nValue) - ((tempLCM / inValue2.dValue) * inValue2.nValue);
    temp.dValue = tempLCM;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator*(Rational const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue * inValue2.nValue;
    temp.dValue = inValue1.dValue * inValue2.dValue;
    temp.setLowestTerm();
    return temp;
  }

  Rational const operator/(Rational const& inValue1, Rational const& inValue2) {
    Rational temp;
    temp.nValue = inValue1.nValue * inValue2.dValue;
    temp.dValue = inValue1.dValue * inValue2.nValue;
    temp.setLowestTerm();
    return temp;
  }

//relational operators

  bool operator<(Rational const& inValue1, int const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int < inValue2) return true;
    else return false;
  }

  bool operator<=(Rational const& inValue1, int const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int < inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem == 0) return true;
    else return false;
  }

  bool operator>(Rational const& inValue1, int const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int > inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem > 0) return true;
    else return false;
  }

  bool operator>=(Rational const& inValue1, int const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int > inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem >= 0) return true;
    else return false;
  }

  bool operator==(Rational const& inValue1, int const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int == inValue2 && inValue1_rem == 0) return true;
    else return false;
  }

  bool operator<(int const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 < inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem > 0) return true;
    else return false;
  }

  bool operator<=(int const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 < inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem >= 0) return true;
    else return false;
  }

  bool operator>(int const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 > inValue2_int) return true;
    else return false;
  }

  bool operator>=(int const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 > inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem == 0) return true;
    else return false;
  }

  bool operator==(int const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 == inValue2_int && inValue2_rem == 0) return true;
    else return false;
  }

  bool operator<(Rational const& inValue1, Integer const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int < inValue2) return true;
    else return false;
  }

  bool operator<=(Rational const& inValue1, Integer const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int < inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem == 0) return true;
    else return false;
  }

  bool operator>(Rational const& inValue1, Integer const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int > inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem > 0) return true;
    else return false;
  }

  bool operator>=(Rational const& inValue1, Integer const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int > inValue2) return true;
    else if (inValue1_int == inValue2 && inValue1_rem >= 0) return true;
    else return false;
  }

  bool operator==(Rational const& inValue1, Integer const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    if (inValue1_int == inValue2 && inValue1_rem == 0) return true;
    else return false;
  }

  bool operator<(Integer const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 < inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem > 0) return true;
    else return false;
  }

  bool operator<=(Integer const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 < inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem >= 0) return true;
    else return false;
  }

  bool operator>(Integer const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 > inValue2_int) return true;
    else return false;
  }

  bool operator>=(Integer const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 > inValue2_int) return true;
    else if (inValue1 == inValue2_int && inValue2_rem == 0) return true;
    else return false;
  }

  bool operator==(Integer const& inValue1, Rational const& inValue2) {
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1 == inValue2_int && inValue2_rem == 0) return true;
    else return false;
  }

  bool operator<(Rational const& inValue1, Rational const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1_int < inValue2_int) return true;
    else if (inValue1_int == inValue2_int && inValue1_rem < inValue2_rem) return true;
    else return false;
  }

  bool operator<=(Rational const& inValue1, Rational const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1_int < inValue2_int) return true;
    else if (inValue1_int == inValue2_int && inValue1_rem <= inValue2_rem) return true;
    else return false;
  }

  bool operator>(Rational const& inValue1, Rational const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1_int > inValue2_int) return true;
    else if (inValue1_int == inValue2_int && inValue1_rem > inValue2_rem) return true;
    else return false;
  }

  bool operator>=(Rational const& inValue1, Rational const& inValue2) {
    Integer inValue1_int;
    Integer inValue1_rem;
    Integer inValue2_int;
    Integer inValue2_rem;
    inValue1_int = inValue1.nValue / inValue1.dValue;
    inValue1_rem = inValue1.nValue % inValue1.dValue;
    inValue2_int = inValue2.nValue / inValue2.dValue;
    inValue2_rem = inValue2.nValue % inValue2.dValue;
    if (inValue1_int > inValue2_int) return true;
    else if (inValue1_int == inValue2_int && inValue1_rem >= inValue2_rem) return true;
    else return false;
  }

  bool operator==(Rational const& inValue1, Rational const& inValue2) {
    if (inValue1.nValue == inValue2.nValue && inValue1.dValue == inValue2.dValue) return true;
    return false;
  }

  Rational convertQfromZ(Integer const& inValue) {
    Rational temp;
    temp.setNumerator(inValue);
    temp.setDenominator(1);
    return temp;
  }

// private functions

  void Rational::checkDenominator() {
    if (dValue < 0) {
      nValue = nValue * -1;
      dValue = abs(dValue);
    }
    else if (dValue == 0) {
      cout << nValue << "/" << dValue << " is undefined.\n";
    }
  }

  void Rational::setLowestTerm() {
    Integer gcdTemp;
    gcdTemp = gcd(nValue,dValue);
    nValue = nValue / gcdTemp;
    dValue = dValue / gcdTemp;
  }

}

#endif
